// >$ gcc convert_hexchar_to_byte.c WakeOnLAN.c && ./a.out
// https://www.dell.com/support/kbdoc/zh-cn/000175283/%E5%A6%82%E4%BD%95-%E5%9C%A8-%E6%82%A8%E7%9A%84-%E6%88%B4%E5%B0%94-%E7%B3%BB%E7%BB%9F-%E4%B8%8A-%E8%AE%BE%E7%BD%AE-%E9%80%9A%E8%BF%87-lan-%E5%94%A4%E9%86%92-wol?lang=zh

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include "endian.h"

#define BIND_LOCAL_PORT (0)

#define SERVER_ADDR "255.255.255.255"
#define WOL_PORT_1 (7)
#define WOL_PORT_2 (9)
#if BIND_LOCAL_PORT
    #define LOCAL_PORT  (14008)
#endif

#define NETMASK "255.255.255.0"

#define BUFSIZE (6 * 18)
#define TARGET_MAC (0x54BF646B5E12LL)

#ifndef MIN
    #define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

extern void show_hex(const char *tag, const void *data, size_t len);

static void __fillData(char *buf, long long MAC)
{
    int i;

    memset(buf, 0xFF, 6);
    
    for (i = 1; i <= 16; i++)
    {
        memcpy(buf + i * 6, &MAC, 6);
    }

    memset(buf + i * 6, 0x00, 6);
}

static void __sendData(const char *toIP, short port, const char *data, int len)
{
    int sockfd = -1;
    int optval = 1;
#if BIND_LOCAL_PORT
    struct sockaddr_in localAddr;
#endif
    struct sockaddr_in remoteAddr;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket()");
        goto Clean;
    }

    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)))
    {
        perror("SOCK_BROADCAST");
    }

#if BIND_LOCAL_PORT
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
    {
        perror("SO_REUSEADDR");
    }

    localAddr.sin_family = AF_INET;
    localAddr.sin_port = htons(LOCAL_PORT);
    inet_pton(AF_INET, "0.0.0.0", &localAddr.sin_addr);

    if (bind(sockfd, (struct sockaddr *)&localAddr, sizeof(localAddr)))
    {
        perror("bind()");
        goto Clean;
    }
#endif

    remoteAddr.sin_family = AF_INET;
    remoteAddr.sin_port = htons(port);
    inet_pton(AF_INET, toIP, &remoteAddr.sin_addr);

    printf("sendTo: %s:%d\n", toIP, port);

    if (sendto(sockfd, data, BUFSIZE, 0, \
        (struct sockaddr *)&remoteAddr, sizeof(remoteAddr)) < 0)
    {
        perror("sendto");
        goto Clean;
    }

Clean:

    if (sockfd >= 0) {
        close(sockfd);
    }
}

static void __getBroadcastAddr(const char *ifName, char *ip)
{
    int sockfd = -1;
    struct ifreq ifreq;
    const char *tmpIP = NULL;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket()");
        goto Clean;
    }

    memset(ip, 0x00, 16); // xxx.xxx.xxx.xxx\0
    memset(ifreq.ifr_name, 0, sizeof(ifreq.ifr_name));
    memcpy(ifreq.ifr_name, ifName, MIN(strlen(ifName), sizeof(ifreq.ifr_name)));

    if (ioctl(sockfd , SIOCGIFBRDADDR, &ifreq))
    {
        fprintf(stderr, "ioctl [%s]: %s\n", ifName, strerror(errno));
        goto Clean;
    }
    tmpIP = inet_ntoa(((struct sockaddr_in *) (&ifreq.ifr_addr))->sin_addr);
    printf("%s: %s\n", &ifreq.ifr_name, tmpIP);
    strcpy(ip, tmpIP);

Clean:

    if (sockfd >= 0) {
        close(sockfd);
    }
}

static void __getAllBroadcastAddr(void)
{
    int sockfd = -1;
    int interface = -1;

    struct ifreq ifreqAry[16];
    struct ifconf ifconf;
    const char *ip = NULL;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket()");
        goto Clean;
    }

    ifconf.ifc_len = sizeof(ifreqAry);
    ifconf.ifc_buf = (caddr_t)ifreqAry;
    if (ioctl(sockfd, SIOCGIFCONF, &ifconf))
    {
        perror("ioctl(SIOCGIFCONF)");
        goto Clean;
    }

    interface = ifconf.ifc_len / sizeof(struct ifreq);
    printf("interface=%d\n", interface);

    while (interface-- > 0)
    {
        // https://blog.csdn.net/qq_37858386/article/details/130108566
        // https://blog.51cto.com/u_15346415/3692495
        if (ioctl(sockfd , SIOCGIFBRDADDR, &ifreqAry[interface]))
        {
            fprintf(stderr, "ioctl [%d][%s]: %s\n", \
                interface, ifreqAry[interface].ifr_name, strerror(errno));
            goto Clean;
        }
        ip = inet_ntoa(((struct sockaddr_in *) (&ifreqAry[interface].ifr_addr))->sin_addr);
        printf("%s: %s\n", &ifreqAry[interface].ifr_name, ip);
    }

Clean:

    if (sockfd >= 0) {
        close(sockfd);
    }
}

int main(int argc, char *argv[])
{
    char *buf = NULL;
    char IP[16];

    buf = (char *) malloc(BUFSIZE);
    if (!buf)
    {
        fprintf(stderr, "No memory!\n");
        goto Clean;
    }

    long long bigendMAC = swap64(TARGET_MAC) >> 16;
    // long long bigendMAC = TARGET_MAC;
    __fillData(buf, bigendMAC);
    show_hex("Package", buf, BUFSIZE);

    __getBroadcastAddr("br0", IP);

    __sendData(SERVER_ADDR, WOL_PORT_1, buf, BUFSIZE);
    __sendData(SERVER_ADDR, WOL_PORT_2, buf, BUFSIZE);
    __sendData(IP, WOL_PORT_1, buf, BUFSIZE);
    __sendData(IP, WOL_PORT_2, buf, BUFSIZE);

Clean:

    if (buf)
    {
        free(buf);
    }

    return errno;
}
